<?php

abstract class CronJob {
	private $enabled = true;
  private $job_name;
	private $shutdown_functions = array();

  public function __construct($job_name) {
    $this->job_name = $job_name;
  }

	private function disable($condition) {
		if ($condition) $this->enabled = false;
		return $this;
	}

	public function onProduction() {
		return $this->disable( !ON_PRODUCTION );
	}

	public function onMinute($start_minute, $repeat_period = 60) {
		if($repeat_period > 60) throw new Exception("repeat_period should below 60, use onHour() instead !");
    $disable = (date('i') - $start_minute) % $repeat_period != 0;
		return $this->disable($disable);
	}

	public function onHour($start_hour, $repeat_period = 24) {
		if($repeat_period > 24) throw new Exception("repeat_period should below 24, you may need a new function!");
    $disable = (date('H') - $start_hour) % $repeat_period != 0;
		return $this->disable($disable);
	}

	public function isUnique($key = ''){
		$key = get_class($this) . $this->job_name . $key;

		if (Locker::lock($key)) {
			$this->shutdown_functions[] = function() use($key) { Locker::unlock($key); };
		} else {
			$this->disable(true);
		}
		return $this;
	}

	public function doJob() {
		if ($this->enabled) {
			$this->log('Start to run ' . $this->job_name . '...');
			$this->{$this->job_name}();
			foreach($this->shutdown_functions as $func) {
				$func();
			}
			$this->log('Finished ' . $this->job_name . ' !');
		}
	}

	public function log($log_string){
    @file_put_contents(LOG_DIR . '/job_' . get_class($this) . '.log',
        date('Y-m-d H:i:s') . ": {$log_string}\n", FILE_APPEND);
		//SystemLog::local_log('job_' . get_class($this), $log_string);
	}
}

// 单机锁
class Locker {
	private static $file_handlers = array();

	private static function file($key) {
		return LOG_DIR . '/' . md5($key) . '.locker';	//需要避免传入的key可能包含特殊字符，作文件名有问题
	}

	public static function lock($key) {
		if(isset(self::$file_handlers[$key])) return false;
		self::$file_handlers[$key] = fopen(self::file($key), 'w+');
		return flock(self::$file_handlers[$key], LOCK_EX | LOCK_NB);	// 独占锁、非阻塞
	}

	public static function unlock($key) {
		if (isset(self::$file_handlers[$key])) {
			fclose(self::$file_handlers[$key]);
			@unlink(self::file($key));
			unset(self::$file_handlers[$key]);
		}
	}
}
